Provision是什么?
Provision
的作用很简单,就是一个系统初始化引导程序,源生的Android里面Provision
只做了一件事,就是写入一个DEVICE_PROVISIONED
标记。
这个标记作用很大,这个标记只会在系统 全新升级(双清)的时候写入一次,代表了Android系统升级准备完成,可以正常工作。
Provision在哪?
一般在package
目录下:package/apps/Provision
。
Proision代码
Provision
下只有一个Activity
文件和一个Manifest
文件
Manifest
配置说明:
android:excludeFromRecents="true"
不在Recents
界面显示。<intent-filter android:priority="1">
优先级高于Launcher
,所以会先于Launcher
启动(因为系统Launcher没有显式设置priority值,所以默认是0)category.HOME
桌面程序标记,和Launcher
属于一个级别
Activity
public class DefaultActivity extends Activity
{
@Override
protected void onCreate(Bundle icicle)
{
super.onCreate(icicle);
// Add a persistent setting to allow other apps to know the device has been provisioned.
Settings.Secure.putInt(getContentResolver(), Settings.Secure.DEVICE_PROVISIONED, 1);
// remove this activity from the package manager.
PackageManager pm = getPackageManager();
ComponentName name = new ComponentName(this, DefaultActivity.class);
pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
// terminate the activity.
finish();
}
}
源生代码很简单
做了两件事:
- 设置
DEVICE_PROVISIONED
标记 - 禁止
Provision
自己的Activity
组件
对这个代码做点说明:
// Add a persistent setting to allow other apps to know the device has been provisioned.
Settings.Secure.putInt(getContentResolver(), Settings.Secure.DEVICE_PROVISIONED, 1);
注释表述很清楚:为设备写入持久的数据,并供其他App了解设备已经provision过。
class Settings
The Settings provider contains global system-level device preferences.
class Secure
Secure system settings, containing system preferences that applications can read but are not allowed to write. These are for preferences that the user must explicitly modify through the system UI or specialized APIs for those values, not modified directly by applications.
public static boolean putInt(ContentResolver cr, String name, int value)
Convenience function for updating a single settings value as an integer. This will either create a new entry in the table if the given name does not exist, or modify the value of the existing row with that name. Note that internally setting values are always stored as strings, so this function converts the given value to a string before storing it.
- @param cr The ContentResolver to access.
- @param name The name of the setting to modify.
- @param value The new value for the setting.
- @return true if the value was set, false on database errors
禁止组件的功能
pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
public abstract void setComponentEnabledSetting(ComponentName componentName,
int newState, int flags);Set the enabled setting for a package component (activity, receiver, service, provider). This setting will override any enabled state which may have been set by the component in its manifest.
@param componentName The component to enable
@param newState The new enabled state for the component. The legal values for this state are:
COMPONENT_ENABLED_STATE_ENABLED
COMPONENT_ENABLED_STATE_DISABLED
COMPONENT_ENABLED_STATE_DEFAULT
The last one removes the setting, thereby restoring the component’s state to whatever was set in it’s manifest (or enabled, by default).
@param flags Optional behavior flags: DONT_KILL_APP or 0.
功能禁止后,系统package信息会记录下来,保存在 /data/system/packages.xml
这段代码就是记录的禁止DefaultActivity
的信息。所以这个组件只会运行一次,之后就被禁止运行了。除非格式化/data/
目录。
5X实现
/**
* Application that sets the provisioned bit, like SetupWizard does.
*/
public class DefaultActivity extends Activity {
private static final String ORIGINAL_LAUNCHER_PACKAGENAME = "com.android.launcher3";
private static final String ORIGINAL_LAUNCHER_CLASSNAME = "com.android.launcher3.Launcher";
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
// Add a persistent setting to allow other apps to know the device has been provisioned.
Settings.Global.putInt(getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 1);
Settings.Secure.putInt(getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 1);
// remove this activity from the package manager.
PackageManager pm = getPackageManager();
ComponentName name = new ComponentName(this, DefaultActivity.class);
pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
//set Launcher3 as the preferred home activity
setupDefaultLauncher(pm);
// terminate the activity.
finish();
}
private void setupDefaultLauncher(PackageManager pm){
Intent queryIntent = new Intent();
queryIntent.addCategory(Intent.CATEGORY_HOME);
queryIntent.setAction(Intent.ACTION_MAIN); // 查找符合 Launcher 的 activity
List homeActivities = pm.queryIntentActivities(queryIntent, 0);
if(homeActivities == null) {
return;
}
ComponentName defaultLauncher = new ComponentName(ORIGINAL_LAUNCHER_PACKAGENAME,
ORIGINAL_LAUNCHER_CLASSNAME);
int activityNum = homeActivities.size();
ComponentName[] set = new ComponentName[activityNum];
int defaultMatch = -1;
for(int i = 0; i < activityNum; i++){ //从符合 Launcher 中查找 Launcher3
ResolveInfo info = homeActivities.get(i);
set[i] = new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
if(ORIGINAL_LAUNCHER_CLASSNAME.equals(info.activityInfo.name)
&& ORIGINAL_LAUNCHER_PACKAGENAME.equals(info.activityInfo.packageName)){
defaultMatch = info.match;
}
}
//if Launcher3 is not found, do not set anything
if(defaultMatch == -1){
return;
}
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_MAIN);
filter.addCategory(Intent.CATEGORY_HOME);
filter.addCategory(Intent.CATEGORY_DEFAULT);
pm.addPreferredActivity(filter, defaultMatch, set, defaultLauncher);
}
}
Reference
Android 初始化Setup Wizard——Provision